home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / BLOCKS.PAK / BLOCKS.CPP next >
C/C++ Source or Header  |  1997-05-06  |  16KB  |  543 lines

  1. //--------------------------------------------------------------------------
  2. // Turbo Blocks -- Copyright (c) 1995, Borland International
  3. //--------------------------------------------------------------------------
  4. #include <owl/pch.h>
  5. #include <owl/applicat.h>
  6. #include <owl/framewin.h>
  7. #include <owl/window.h>
  8. #include <owl/dialog.h>
  9. //#include <mmsystem.h>
  10.  
  11. #include <string.h>
  12.  
  13. #include "blocks.rh"
  14.  
  15. const int GAME_WIDTH = 10;
  16. const int GAME_HEIGHT = 20;
  17. const char IniFilename[] = "BLOCKS.INI";
  18.  
  19. // TBlock is a structure which defines the geometric game piece
  20. //
  21. struct TBlock {
  22.   int  size;          // 2x2, 3x3, or 4x4 (size of square which completely
  23.                       // contains the piece)
  24.   char elements[17];
  25.  
  26.   void rotate();
  27. };
  28.  
  29. // Rotate -- rotate the block (only rotates one direction)
  30. //
  31. void TBlock::rotate() {
  32.   int i,j;
  33.   char tempBlock[17];
  34.  
  35.   switch (size) {
  36.     case 4:
  37.       strcpy(tempBlock,elements);
  38.       for (i=0;i<4;i++)
  39.        for (j=0;j<4;j++)
  40.          tempBlock[i*4+j]=elements[j*4+i];
  41.       strcpy(elements,tempBlock);
  42.       break;
  43.  
  44.     case 3:
  45.       strcpy(tempBlock,elements);
  46.       for (i=0;i<3;i++)
  47.        for (j=0;j<3;j++)
  48.          tempBlock[(2-i)*4+j]=elements[j*4+i];
  49.       strcpy(elements,tempBlock);
  50.       break;
  51.  
  52.     case 2:  // no 2x2 blocks can rotate, so do nothing
  53.       break;
  54.   }
  55. }
  56.  
  57. // Define all the game pieces.
  58.  
  59. TBlock blocks[7] = { {4, " *  "
  60.                          " *  "
  61.                          " *  "
  62.                          " *  "},
  63.                      {3, " ** "
  64.                          " *  "
  65.                          " *  "
  66.                          "    "},
  67.                      {3, "**  "
  68.                          " *  "
  69.                          " *  "
  70.                          "    "},
  71.                      {3, " *  "
  72.                          "*** "
  73.                          "    "
  74.                          "    "},
  75.                      {3, "**  "
  76.                          " ** "
  77.                          "    "
  78.                          "    "},
  79.                      {3, " ** "
  80.                          "**  "
  81.                          "    "
  82.                          "    "},
  83.                      {2, "**  "
  84.                          "**  "
  85.                          "    "
  86.                          "    "} };
  87.  
  88. // these define the pens and brushes to be used for the various
  89. // blocks.  each block type is a different color
  90. //
  91.  
  92. TPen pen[8] = { TPen( TColor( 0, 0, 64 ) ),
  93.                 TPen( TColor( 0, 0, 255 ) ),
  94.                 TPen( TColor( 0, 255, 0 ) ),
  95.                 TPen( TColor( 255, 0, 0 ) ),
  96.                 TPen( TColor( 255,255,0 ) ),
  97.                 TPen( TColor( 255,0,255 ) ),
  98.                 TPen( TColor( 0,255,255 ) ),
  99.                 TPen( TColor( 255,255,255 ) ) };
  100.  
  101. TBrush* brush[8];
  102.  
  103.  
  104. // BlocksWindow is the class that actually defines the game.  It is used
  105. // as the client window inside of a TFrameWindow.
  106. //
  107. class TBlocksWindow: public TWindow {
  108.  
  109.   enum GameState { gsGameOver,         // game over, wait for newgame command
  110.                    gsBlockDropping,    // normal mode, blocks are dropping
  111.                    gsPaused };         // game is paused
  112.  
  113.   TMemoryDC *memDC;
  114.   GameState gameState;                       // current state of the game
  115.   TBlock    currentBlock;                    // currently falling block
  116.   int       x,y,                             // current block position
  117.             color;                           // current block color
  118.   int       board[GAME_HEIGHT+1][GAME_WIDTH+2];  // the game grid
  119.                                              // edge to make for easier edge detection
  120.   uint      Timer;                           // ID of game timer
  121.   uint      dropCount;                       // countdown to next time piece falls one row
  122.  
  123.   bool      dropping;                        // is the user holding the 'drop' key down?
  124.  
  125.   int       gameSpeed,blockSize;             // read from .INI file
  126. public:
  127.   TBlocksWindow( TWindow* parent );
  128.   ~TBlocksWindow() {
  129.     char temp[10];
  130.     wsprintf(temp,"%d",gameSpeed);
  131.     WritePrivateProfileString("blocks","gamespeed",temp,IniFilename);
  132.     wsprintf(temp,"%d",blockSize);
  133.     WritePrivateProfileString("blocks","blocksize",temp,IniFilename);
  134.     delete memDC;
  135.   }
  136.   bool EvEraseBkgnd( HDC ) {    // since we paint the entire window, we
  137.     return TRUE;                // don't want windows doing it for us.
  138.   }                             // this will reduce flicker
  139.  
  140.   void SetupWindow();
  141.   void CleanupWindow() {
  142.     KillTimer( Timer );
  143.   }
  144.   void Pause();
  145.   void PauseEnabler( TCommandEnabler& tce ) {
  146.     if (gameState==gsGameOver)
  147.       tce.Enable( false );
  148.     else
  149.       if (gameState==gsPaused)
  150.         tce.SetText("Resume\tAlt+P");
  151.       else
  152.         tce.SetText("Pause\tAlt+P");
  153.   }
  154.   void NewGame();
  155.   void ClearBoard();
  156.   void NewBlock( int blockType );
  157.   void RemoveLines();
  158.   void PlaceBlock();
  159.   bool HitTest( TBlock& block, int x, int y );
  160.   void DrawBlock( TDC& dc, TBlock& block, TPoint& pos );
  161.   void Paint( TDC&, bool, TRect& );
  162.   void EvKeyDown( uint key, uint repeatCount, uint flags );
  163.   void EvKeyUp( uint, uint, uint );
  164.   void EvTimer( uint id );
  165.  
  166.  
  167.   DECLARE_RESPONSE_TABLE( TBlocksWindow );
  168. };
  169.  
  170. DEFINE_RESPONSE_TABLE1( TBlocksWindow, TWindow )
  171.   EV_WM_KEYDOWN,
  172.   EV_WM_KEYUP,
  173.   EV_WM_ERASEBKGND,
  174.   EV_WM_TIMER,
  175.   EV_COMMAND( CM_GAMEPAUSE, Pause ),
  176.   EV_COMMAND( CM_GAME_NEW, NewGame ),
  177.   EV_COMMAND_ENABLE( CM_GAMEPAUSE, PauseEnabler ),
  178. END_RESPONSE_TABLE;
  179.  
  180. // TBlocksWindow -- constructor.  Read the options from the INI file,
  181. // set our game window size, and reset the game board.
  182. //
  183. TBlocksWindow::TBlocksWindow( TWindow* parent )  : TWindow( parent ) {
  184.  
  185.   blockSize = GetPrivateProfileInt( "blocks","blocksize",20,IniFilename);
  186.   gameSpeed = GetPrivateProfileInt( "blocks","gamespeed",10,IniFilename);
  187.  
  188.   Attr.W = blockSize*GAME_WIDTH;
  189.   Attr.H = blockSize*GAME_HEIGHT;
  190.  
  191.   ClearBoard();
  192.  
  193.   dropCount = 0;
  194.   gameState = gsGameOver;
  195.   dropping = false;
  196. }
  197.  
  198. // SetupWindow -- after the window is created, we need to do a bit more
  199. // work.  Create a timer to drive the game, and create a memory DC that
  200. // will be used to draw the blocks off-screen.
  201. //
  202. void TBlocksWindow::SetupWindow() {
  203.   TWindow::SetupWindow();
  204.  
  205.   Timer = 0;
  206.   if (SetTimer( 1, 10 ))
  207.     Timer=1;
  208.  
  209.   TClientDC dc( HWindow );
  210.   memDC = new TMemoryDC( dc );
  211.   memDC->SelectObject( TBitmap( dc, GAME_WIDTH*blockSize, GAME_HEIGHT*blockSize ) );
  212. }
  213.  
  214. // RemoveLines -- checks for completed lines and removes them from
  215. // the game board.  If lines are removed, higher lines are moved down
  216. // to fill in the space.
  217. //
  218. void TBlocksWindow::RemoveLines() {
  219.   int i,j,k,l;
  220.   bool lineFull,linesRemoved;
  221.  
  222.   linesRemoved=false;
  223.   j=GAME_HEIGHT-1;
  224.   while (j>=0) {
  225.     lineFull=true;
  226.     for (i=1;i<=GAME_WIDTH;i++)
  227.       if (board[j][i]==0)
  228.         lineFull=false;
  229.     if (lineFull) {
  230.       linesRemoved=true;
  231.       for (k=j;k>=1;k--)
  232.         for (l=1;l<=GAME_WIDTH;l++)
  233.           board[k][l]=board[k-1][l];
  234.       for (l=1;l<=GAME_WIDTH;l++)
  235.         board[0][l]=0;
  236.     } else
  237.       j--;
  238.   }
  239.   if (linesRemoved) {
  240. //    sndPlaySound( "tada.wav", SND_ASYNC | SND_NODEFAULT );
  241.     Invalidate();
  242.   }
  243. }
  244.  
  245. // NewGame -- start a new game.  First clear the board, then
  246. // add the first block, and change the game state to gsBlockDropping
  247. //
  248. void TBlocksWindow::NewGame() {
  249.   ClearBoard();
  250.   NewBlock( random(7) );
  251.   gameState = gsBlockDropping;
  252.   Invalidate();
  253. }
  254.  
  255. // ClearBoard -- resets the game board to be empty
  256. //
  257. void TBlocksWindow::ClearBoard() {
  258.   int i,j;
  259.  
  260.   for (i=0;i<(GAME_HEIGHT+1);i++)
  261.     for (j=0;j<(GAME_WIDTH+2);j++)
  262.       board[i][j]=0;
  263.   for (i=0;i<(GAME_HEIGHT+1);i++) {
  264.     board[i][0] = -1;
  265.     board[i][GAME_WIDTH+1] = -1;
  266.   }
  267.   for (j=0;j<(GAME_WIDTH+2);j++)
  268.     board[GAME_HEIGHT][j]=-1;
  269. }
  270.  
  271. // EvTimer -- called by the timer we created in SetupWindow.  Depending
  272. // on the game state, do various things.
  273. //
  274. void TBlocksWindow::EvTimer( uint ) {
  275.  
  276.   switch (gameState) {
  277.     case gsGameOver:            // no game in progress, do nothing
  278.       break;
  279.  
  280.     case gsBlockDropping:       // game in progress
  281.       dropCount++;                             // increment drop counter
  282.       if ((dropping) ||
  283.           (dropCount==gameSpeed)) {            // if time to drop
  284.         dropCount=0;                           // reset counter
  285.         y++;                                   // move block down
  286.         if (HitTest( currentBlock, x, y ) ) {  // if it hit something
  287.           y--;                                 // move it back up
  288.           PlaceBlock();                        // make it permanent
  289.           RemoveLines();
  290.           NewBlock( random(7) );
  291.          }
  292.         Invalidate();                          // redraw game board
  293.       }
  294.       break;
  295.  
  296.     case gsPaused:            // game is paused
  297.       break;
  298.   }
  299. }
  300.  
  301. // EvKeyUp/EvKeyDown -- respond to key press/release messages.  For the
  302. // 'drop' key, we set a flag whenever the key is held down.  For the
  303. // left/right/rotate keys, we only keep track of keydown events.
  304. //
  305. void TBlocksWindow::EvKeyUp( uint key, uint, uint ) {
  306.   switch (key) {
  307.     case VK_NUMPAD2:
  308.     case VK_DOWN:
  309.       dropping = false;
  310.       break;
  311.   }
  312. }
  313.  
  314. void TBlocksWindow::EvKeyDown( uint key, uint /*repeat*/, uint /*flags*/ ) {
  315.   if ( gameState == gsPaused ) {
  316.     gameState = gsBlockDropping;
  317.     Invalidate();
  318.     return;
  319.   }
  320.   switch (key) {
  321.  
  322.     // move block left
  323.  
  324.     case VK_NUMPAD4:
  325.     case VK_LEFT:
  326.       x--;
  327.       if (HitTest( currentBlock, x, y ))
  328.         x++;
  329.      else
  330.        Invalidate();
  331.      break;
  332.  
  333.     // move block right
  334.  
  335.     case VK_NUMPAD6:
  336.     case VK_RIGHT:
  337.       x++;
  338.       if (HitTest( currentBlock, x, y ))
  339.         x--;
  340.       else
  341.         Invalidate();
  342.       break;
  343.  
  344.     // turn on fast dropping
  345.  
  346.     case VK_NUMPAD2:
  347.     case VK_DOWN:
  348.       dropping = true;
  349.       break;
  350.  
  351.     // rotate block
  352.  
  353.     case VK_UP:
  354.     case VK_NUMPAD5:
  355.     case VK_SPACE:
  356.       currentBlock.rotate();
  357.       if (HitTest( currentBlock, x, y )) {
  358.         currentBlock.rotate();
  359.         currentBlock.rotate();
  360.         currentBlock.rotate();
  361.       }
  362.       Invalidate();
  363.       break;
  364.   }
  365. }
  366.  
  367. // Pause -- handler for the 'Pause' menu item
  368. //
  369. void TBlocksWindow::Pause() {
  370.   if (gameState==gsBlockDropping)
  371.     gameState=gsPaused;
  372.   else
  373.     if (gameState==gsPaused)
  374.       gameState=gsBlockDropping;
  375.   Invalidate();
  376. }
  377.  
  378. // HitTest -- tests to see if a block overlaps any occupied square
  379. //            on the board
  380. //
  381. bool TBlocksWindow::HitTest( TBlock& block, int x, int y ) {
  382.   int i,j;
  383.   for (i=0;i<4;i++)
  384.     for (j=0;j<4;j++)
  385.       if ( ((x+i)<(GAME_WIDTH+2)) &&
  386.            ((x+i+1)>=0)  &&
  387.            ((y+j)<(GAME_HEIGHT+1)) &&
  388.            ((y+j)>=0) )             // make sure block in question is in range
  389.         if (board[y+j][x+1+i]!=0)   // if the board piece is empty, skip test
  390.           if (block.elements[j*4+i]=='*')
  391.             return true;
  392.   return false;
  393. }
  394.  
  395. // NewBlock -- creates a new block at the top of the screen
  396. //
  397. void TBlocksWindow::NewBlock( int blockType ) {
  398.   currentBlock = blocks[ blockType ];
  399.   color = blockType+1;
  400.   x = 4;
  401.   y = 0;
  402.   // if the new block hits anything on the screen, the game is over
  403.   //
  404.   if (HitTest( currentBlock, x, y )) {
  405.     PlaceBlock();
  406.     Invalidate();
  407.     gameState = gsGameOver;
  408.   }
  409. }
  410.  
  411. // PlaceBlock -- puts the current block permanently into the board array.
  412. //               this function is called when the block reaches the bottom
  413. //               of the game board.
  414. //
  415. void TBlocksWindow::PlaceBlock() {
  416.   int i,j;
  417.   for (i=0;i<4;i++)
  418.     for (j=0;j<4;j++)
  419.       if (currentBlock.elements[j*4+i]=='*')
  420.         board[y+j][x+1+i]=color;
  421.  
  422.   Invalidate();
  423.   RemoveLines();
  424. }
  425.  
  426. // DrawBlock -- draws an individual game piece.
  427. //
  428. void TBlocksWindow::DrawBlock( TDC& dc, TBlock& block, TPoint& pos ) {
  429.   int i,j,size=block.size;
  430.   dc.SelectObject( *brush[ color ] );
  431.   dc.SelectObject( pen[ color ] );
  432.   for (i=0;i<size;i++)
  433.     for (j=0;j<size;j++)
  434.       if (block.elements[j*4+i]=='*')
  435.         dc.Rectangle( pos.x*blockSize+i*blockSize,
  436.                       pos.y*blockSize+j*blockSize,
  437.                       pos.x*blockSize+i*blockSize+blockSize,
  438.                       pos.y*blockSize+j*blockSize+blockSize );
  439. }
  440.  
  441. // Paint -- redraws the entire window.  Currently, whenever anything
  442. // on the game board changes, we redraw the entire board.  This could
  443. // obviously be improved, by only redrawing the area around the moving
  444. // block.
  445. //
  446. void TBlocksWindow::Paint( TDC& dc, bool /*erase*/, TRect& /*rect*/ ) {
  447.   int i,j;
  448.  
  449.   // if the game is paused, blank the screen (to prevent cheating!)
  450.  
  451.   if (gameState==gsPaused) {
  452.     memDC->FillRect( GetClientRect(), *brush[0] );
  453.     memDC->SetTextColor( TColor( 0, 255, 255 ) );
  454.     memDC->SetBkColor( TColor( 0, 0, 128 ) );
  455.     memDC->TextOut( blockSize*2,blockSize*9, " * * P A U S E D * * " );
  456.     dc.BitBlt( GetClientRect(), *memDC, TPoint(0,0) );
  457.     return;
  458.   }
  459.  
  460.   // clear the memory DC
  461.  
  462.   memDC->FillRect( GetClientRect(), *brush[0] );
  463.  
  464.   // draw the permanent blocks
  465.  
  466.   for (j=0;j<GAME_HEIGHT;j++)
  467.     for (i=0;i<GAME_WIDTH;i++)
  468.       if ( board[j][i+1]!=0 ){
  469.         memDC->SelectObject( pen[ board[j][i+1] ] );
  470.         memDC->SelectObject( *brush[ board[j][i+1] ] );
  471.         memDC->Rectangle( i*blockSize,j*blockSize,
  472.                          i*blockSize+blockSize,j*blockSize+blockSize );
  473.       }
  474.  
  475.   // display the game over message if the game has ended
  476.  
  477.   if (gameState == gsGameOver) {
  478.     memDC->SetTextColor( TColor( 0, 255, 255 ) );
  479.     memDC->SetBkColor( TColor( 0, 0, 128 ) );
  480.     memDC->TextOut( blockSize*1.5,blockSize*9, "* G A M E   O V E R *");
  481.   }
  482.  
  483.   // if a block is dropping, draw it
  484.  
  485.   if (gameState == gsBlockDropping)
  486.     DrawBlock( *memDC, currentBlock, TPoint( x, y ) );
  487.  
  488.   // now copy the memory DC to the screen.
  489.  
  490.   dc.BitBlt( GetClientRect(), *memDC, TPoint(0,0) );
  491. }
  492.  
  493. // TBlocksApp -- the main application.  InitMainWindow creates the
  494. // framewindow, and inserts a TBlocksWindow as the client.  The app
  495. // also responds to the 'Game|Exit' and 'Help|About' menu items.
  496. //
  497. class TBlocksApp: public TApplication {
  498. public:
  499.   void InitMainWindow() {
  500.      TFrameWindow *fw = new TFrameWindow( 0, "Turbo Blocks",
  501.                                           new TBlocksWindow(0), true );
  502.      fw->Attr.Style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
  503.      fw->AssignMenu( MAIN_MENU );
  504.      fw->Attr.AccelTable = ACCELERATORS_1;
  505.      fw->SetIcon( this, ICON_1 );
  506.  
  507.      SetMainWindow( fw );
  508.   }
  509.   void AboutBox() {
  510.      TDialog( GetMainWindow(), ABOUT_BOX ).Execute();
  511.   }
  512.   void Exit() {
  513.      GetMainWindow()->CloseWindow();
  514.   }
  515.   DECLARE_RESPONSE_TABLE( TBlocksApp );
  516. };
  517. DEFINE_RESPONSE_TABLE1( TBlocksApp, TApplication )
  518.   EV_COMMAND( CM_HELP_ABOUT, AboutBox ),
  519.   EV_COMMAND( CM_GAMEEXIT, Exit ),
  520. END_RESPONSE_TABLE;
  521.  
  522. // the main program
  523.  
  524. int OwlMain( int, char *[] ) {
  525.  
  526.   brush[0] = new TBrush( TColor( 0, 0, 64 ) );
  527.   brush[1] = new TBrush( TColor( 0, 0, 128 ) );
  528.   brush[2] = new TBrush( TColor( 0, 128, 0 ) );
  529.   brush[3] = new TBrush( TColor( 128, 0, 0 ) );
  530.   brush[4] = new TBrush( TColor( 128,128,0 ) );
  531.   brush[5] = new TBrush( TColor( 128,0,128 ) );
  532.   brush[6] = new TBrush( TColor( 0,128,128 ) );
  533.   brush[7] = new TBrush( TColor( 128,128,128 ) );
  534.  
  535.   randomize();
  536.   int retVal = TBlocksApp().Run();
  537.  
  538.   for (int i = 0; i < 8 ; i++) {
  539.     delete brush[i];
  540.   }
  541.   return retVal;
  542. }
  543.